﻿namespace NTLib.Core.Garbadge
{
    using NTLib.Core.Threading;
    using System;
    using System.Threading.Tasks;

    /// <summary>
    /// Helper used to monitor the garbadge collector action.
    /// </summary>
    public static class GCHelper
    {
        #region Fields

        private static readonly ReaderWriterLocker s_locker;
        private static Action s_GCEnded;
        private static bool s_run;

        #endregion


        #region Ctor

        /// <summary>
        /// Initializes the <see cref="GCHelper"/> class.
        /// </summary>
        static GCHelper()
        {
            s_locker = new ReaderWriterLocker();
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a value indicating whether this instance is running.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is running; otherwise, <c>false</c>.
        /// </value>
        public static bool IsRunning
        {
            get
            {
                if (s_locker.IsDisposed)
                    return false;

                using (s_locker.ScopeLock(true))
                {
                    return s_run;
                }
            }
        }

        #endregion

        #region Event

        /// <summary>
        /// Occurs when the gc end his clean loop.
        /// </summary>
        public static event Action GCEnded
        {
            add
            {
                s_GCEnded += value;

                if (s_locker.IsDisposed)
                    return;

                using (s_locker.ScopeLock())
                {
                    if (s_run == false)
                    {
                        s_run = true;
                        Task.Run(() => GCLoop());
                    }
                }
            }
            remove
            {
                s_GCEnded -= value;

                if (s_GCEnded == null && !s_locker.IsDisposed)
                {
                    using (s_locker.ScopeLock())
                    {
                        if (s_run)
                            s_run = false;
                    }
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Monitor the garbadge collector clean.
        /// </summary>
        private static void GCLoop()
        {
            double last = 0;
            bool localSrun = false;

            if (s_locker.IsDisposed)
                return;

            using (s_locker.ScopeLock(true))
            {
                localSrun = s_run;
            }

            do
            {
                double count = 0;
                for (int i = 0; i < GC.MaxGeneration; ++i)
                    count += GC.CollectionCount(i);

                GC.WaitForPendingFinalizers();

                if (count != last)
                {
                    if (s_GCEnded != null)
                        s_GCEnded();
                }
                last = count;

                if (s_locker.IsDisposed)
                    return;

                using (s_locker.ScopeLock(true))
                {
                    localSrun = s_run;
                }
            }
            while (localSrun);
        }


        #endregion
    }
}
